package wallet

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/zeromicro/go-zero/core/stores/cache"
	"github.com/zeromicro/go-zero/core/stores/sqlx"
	"navi-mumbai/common/gensql"
	"navi-mumbai/common/key/dbkey"
)

var _ UserWalletModel = (*customUserWalletModel)(nil)

type (
	// UserWalletModel is an interface to be customized, add more methods here,
	// and implement the added methods in customUserWalletModel.
	UserWalletModel interface {
		userWalletModel
		Trans(ctx context.Context, fn func(context context.Context, tx sqlx.Session) error) error
		Find(ctx context.Context, uid int64, pageNo, pageSize int64) ([]*UserWallet, error)
		InsertTrans(ctx context.Context, tx sqlx.Session, data *UserWallet) (sql.Result, error)
		AddAmountTrans(ctx context.Context, tx sqlx.Session, amount int64, id int64, uid, moneyType int64) error
		DecrAmountTrans(ctx context.Context, tx sqlx.Session, amount int64, id int64, uid, moneyType int64) error
	}

	customUserWalletModel struct {
		*defaultUserWalletModel
		genSql *gensql.GenSql
	}
)

// NewUserWalletModel returns a model for the database table.
func NewUserWalletModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) UserWalletModel {
	return &customUserWalletModel{
		defaultUserWalletModel: newUserWalletModel(conn, c, opts...),
		genSql:                 gensql.NewGenSql(),
	}
}

func (m *customUserWalletModel) Trans(ctx context.Context, fn func(context context.Context, tx sqlx.Session) error) error {
	return m.TransactCtx(ctx, func(ctx context.Context, tx sqlx.Session) error {
		return fn(ctx, tx)
	})
}

func (m *customUserWalletModel) Find(ctx context.Context, uid int64, pageNo, pageSize int64) ([]*UserWallet, error) {
	// 分页
	if pageNo < 1 {
		pageNo = 1
	}
	if pageSize <= 0 {
		pageSize = 10
	}
	q := fmt.Sprintf("select %s from %s ", userWalletRows, m.table)
	m.genSql.InitSql(q)

	query, values, err := m.genSql.Where("uid = ?", uid).Where("state = ?", dbkey.Enable).OrderBy("create_time DESC").Limit(uint64(pageSize)).Offset(uint64((pageNo - 1) * pageSize)).ToSql()
	if err != nil {
		return nil, err
	}

	resp := make([]*UserWallet, 0)
	err = m.QueryRowsNoCacheCtx(ctx, &resp, query, values...)
	switch err {
	case nil:
		return resp, nil
	default:
		return nil, err
	}
}

func (m *customUserWalletModel) InsertTrans(ctx context.Context, tx sqlx.Session, data *UserWallet) (sql.Result, error) {
	mumbaiUserWalletIdKey := fmt.Sprintf("%s%v", cacheMumbaiWalletUserWalletIdPrefix, data.Id)
	mumbaiUserWalletUidMoneyTypeKey := fmt.Sprintf("%s%v:%v", cacheMumbaiWalletUserWalletUidMoneyTypePrefix, data.Uid, data.MoneyType)
	ret, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
		query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?)", m.table, userWalletRowsExpectAutoSet)
		return tx.ExecCtx(ctx, query, data.Uid, data.MoneyType, data.Amount, data.State)
	}, mumbaiUserWalletIdKey, mumbaiUserWalletUidMoneyTypeKey)
	return ret, err
}

func (m *customUserWalletModel) AddAmountTrans(ctx context.Context, tx sqlx.Session, amount int64, id int64, uid, moneyType int64) error {
	mumbaiWalletUserWalletIdKey := fmt.Sprintf("%s%v", cacheMumbaiWalletUserWalletIdPrefix, id)
	mumbaiWalletUserWalletUidMoneyTypeKey := fmt.Sprintf("%s%v:%v", cacheMumbaiWalletUserWalletUidMoneyTypePrefix, uid, moneyType)
	rs, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
		query := fmt.Sprintf("update %s set amount=amount+? where id = ?", m.table)
		return tx.ExecCtx(ctx, query, amount, id)
	}, mumbaiWalletUserWalletIdKey, mumbaiWalletUserWalletUidMoneyTypeKey)
	if err != nil {
		return err
	}
	ra, err := rs.RowsAffected()
	if err != nil {
		return err
	}
	if ra == 0 {
		return ErrNotFound
	}
	return nil
}

func (m *customUserWalletModel) DecrAmountTrans(ctx context.Context, tx sqlx.Session, amount int64, id int64, uid, moneyType int64) error {
	mumbaiWalletUserWalletIdKey := fmt.Sprintf("%s%v", cacheMumbaiWalletUserWalletIdPrefix, id)
	mumbaiWalletUserWalletUidMoneyTypeKey := fmt.Sprintf("%s%v:%v", cacheMumbaiWalletUserWalletUidMoneyTypePrefix, uid, moneyType)
	rs, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
		query := fmt.Sprintf("update %s set amount = amount - ? where id = ? and amount >= ?", m.table)
		return tx.ExecCtx(ctx, query, amount, id, amount)
	}, mumbaiWalletUserWalletIdKey, mumbaiWalletUserWalletUidMoneyTypeKey)
	if err != nil {
		return err
	}
	ra, err := rs.RowsAffected()
	if err != nil {
		return err
	}
	if ra == 0 {
		return ErrNotFound
	}
	return nil
}
